#include "napi/native_api.h"
#include "vrlib/md_vr_library.h"
#include "vrlib/md_log.h"

#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN 0x3200
#define LOG_TAG "MD360Player"

using namespace asha::vrlib;

struct MD360PlayerWrapper {
    std::shared_ptr<MDVRLibraryAPI> impl;
};

static void Destructor(napi_env env, void* nativeObject, void* finalize_hint) {
    MD360PlayerWrapper* wrapper = reinterpret_cast<MD360PlayerWrapper*>(nativeObject);
    delete wrapper;
}

static napi_value Constructor(napi_env env, napi_callback_info info) {
    napi_value jsThis;
    napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr);

    MD360PlayerWrapper* wrapper = new MD360PlayerWrapper();
    wrapper->impl = MDVRLibraryAPI::CreateLibrary();

    napi_wrap(env, jsThis, wrapper, Destructor, nullptr, nullptr);
    return jsThis;
}

static napi_value SetSurfaceId(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_value jsThis;
    napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);

    MD360PlayerWrapper* wrapper;
    napi_unwrap(env, jsThis, (void**)&wrapper);

    if (wrapper == nullptr || wrapper->impl == nullptr) {
        return nullptr;
    }

    size_t strSize;
    napi_get_value_string_utf8(env, args[0], nullptr, 0, &strSize);
    std::string surfaceId(strSize, '\0');
    napi_get_value_string_utf8(env, args[0], &surfaceId[0], strSize + 1, &strSize);

    int result = wrapper->impl->SetSurfaceId(surfaceId);
    
    napi_value ret;
    napi_create_int32(env, result, &ret);
    return ret;
}

static napi_value RunCmd(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_value jsThis;
    napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);

    MD360PlayerWrapper* wrapper;
    napi_unwrap(env, jsThis, (void**)&wrapper);

    if (wrapper == nullptr || wrapper->impl == nullptr) {
        return nullptr;
    }

    int32_t cmd;
    napi_get_value_int32(env, args[0], &cmd);

    int result = 0;
    switch(cmd) {
        case 1: result = wrapper->impl->Init(); break;
        case 2: result = wrapper->impl->Destroy(); break;
        case 3: result = wrapper->impl->Resume(); break;
        case 4: result = wrapper->impl->Pause(); break;
        default: break;
    }

    napi_value ret;
    napi_create_int32(env, result, &ret);
    return ret;
}

static napi_value UpdateMVPMatrix(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_value jsThis;
    napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);

    MD360PlayerWrapper* wrapper;
    napi_unwrap(env, jsThis, (void**)&wrapper);

    if (wrapper == nullptr || wrapper->impl == nullptr) {
        return nullptr;
    }

    bool isArray;
    napi_is_array(env, args[0], &isArray);
    if (!isArray) return nullptr;

    uint32_t length;
    napi_get_array_length(env, args[0], &length);
    
    float matrix[16];
    for (uint32_t i = 0; i < length && i < 16; i++) {
        napi_value element;
        napi_get_element(env, args[0], i, &element);
        double val;
        napi_get_value_double(env, element, &val);
        matrix[i] = (float)val;
    }

    wrapper->impl->UpdateMVPMatrix(matrix);
    return nullptr;
}

static napi_value GetVideoSurfaceId(napi_env env, napi_callback_info info) {
    napi_value jsThis;
    napi_get_cb_info(env, info, nullptr, nullptr, &jsThis, nullptr);

    MD360PlayerWrapper* wrapper;
    napi_unwrap(env, jsThis, (void**)&wrapper);

    if (wrapper == nullptr || wrapper->impl == nullptr) {
        napi_value nullValue;
        napi_get_null(env, &nullValue);
        return nullValue;
    }

    uint64_t surfaceId = wrapper->impl->GetVideoSurfaceId();
    
    // 将 uint64_t 转换为字符串（因为 surfaceId 在 HarmonyOS 中通常以字符串形式传递）
    char surfaceIdStr[32];
    snprintf(surfaceIdStr, sizeof(surfaceIdStr), "%llu", (unsigned long long)surfaceId);
    
    napi_value result;
    napi_create_string_utf8(env, surfaceIdStr, NAPI_AUTO_LENGTH, &result);
    return result;
}

static napi_value SetClearColor(napi_env env, napi_callback_info info) {
    size_t argc = 4;
    napi_value args[4];
    napi_value jsThis;
    napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);

    MD360PlayerWrapper* wrapper;
    napi_unwrap(env, jsThis, (void**)&wrapper);

    if (wrapper == nullptr || wrapper->impl == nullptr || argc < 4) {
        return nullptr;
    }

    double r, g, b, a;
    napi_get_value_double(env, args[0], &r);
    napi_get_value_double(env, args[1], &g);
    napi_get_value_double(env, args[2], &b);
    napi_get_value_double(env, args[3], &a);

    MD_LOGI("NAPI SetClearColor called: r=%.3f, g=%.3f, b=%.3f, a=%.3f", r, g, b, a);
    wrapper->impl->SetClearColor((float)r, (float)g, (float)b, (float)a);
    return nullptr;
}

static napi_value SetCullFaceEnabled(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_value jsThis;
    napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);

    MD360PlayerWrapper* wrapper;
    napi_unwrap(env, jsThis, (void**)&wrapper);

    if (wrapper == nullptr || wrapper->impl == nullptr || argc < 1) {
        return nullptr;
    }

    bool enabled;
    napi_get_value_bool(env, args[0], &enabled);

    MD_LOGI("NAPI SetCullFaceEnabled called: enabled=%s", enabled ? "true" : "false");
    wrapper->impl->SetCullFaceEnabled(enabled);
    return nullptr;
}

static napi_value SetDepthTestEnabled(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_value jsThis;
    napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);

    MD360PlayerWrapper* wrapper;
    napi_unwrap(env, jsThis, (void**)&wrapper);

    if (wrapper == nullptr || wrapper->impl == nullptr || argc < 1) {
        return nullptr;
    }

    bool enabled;
    napi_get_value_bool(env, args[0], &enabled);

    MD_LOGI("NAPI SetDepthTestEnabled called: enabled=%s", enabled ? "true" : "false");
    wrapper->impl->SetDepthTestEnabled(enabled);
    return nullptr;
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        { "setSurfaceId", nullptr, SetSurfaceId, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "runCmd", nullptr, RunCmd, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "updateMVPMatrix", nullptr, UpdateMVPMatrix, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "getVideoSurfaceId", nullptr, GetVideoSurfaceId, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "setClearColor", nullptr, SetClearColor, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "setCullFaceEnabled", nullptr, SetCullFaceEnabled, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "setDepthTestEnabled", nullptr, SetDepthTestEnabled, nullptr, nullptr, nullptr, napi_default, nullptr }
    };

    napi_value cons;
    napi_define_class(env, "MD360Player", NAPI_AUTO_LENGTH, Constructor, nullptr, sizeof(desc) / sizeof(desc[0]), desc, &cons);

    napi_set_named_property(env, exports, "MD360Player", cons);

    napi_value cmdEnum;
    napi_create_object(env, &cmdEnum);
    
    napi_value val;
    napi_create_int32(env, 0, &val);
    napi_set_named_property(env, cmdEnum, "UNKNOWN", val);
    napi_create_int32(env, 1, &val);
    napi_set_named_property(env, cmdEnum, "INIT", val);
    napi_create_int32(env, 2, &val);
    napi_set_named_property(env, cmdEnum, "DESTROY", val);
    napi_create_int32(env, 3, &val);
    napi_set_named_property(env, cmdEnum, "RESUME", val);
    napi_create_int32(env, 4, &val);
    napi_set_named_property(env, cmdEnum, "PAUSE", val);

    napi_set_named_property(env, exports, "MD360PlayerCmd", cmdEnum);

    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "libmd360player",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterLibmd360playerModule(void)
{
    napi_module_register(&demoModule);
}
